// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module A_cse_ocs_dpa_aes10c_gf16_sq_inv 
   #(
      parameter BETA  = 4'b1011,
      parameter ALPHA = 4'b0001,   
      parameter POLY1 = 3
   )
   (   
      input  logic [3:0] sh_in_rxs,
      input  logic [3:0] sl_in_rxs,
      output logic [3:0] sh_out_wxs,
      output logic [3:0] sl_out_wxs
   );
   
   wire [3:0]         mul1_out_wxs;
   wire [3:0]         inv_out_rxs;
   
   wire [3:0]         sum_sl_sh_wxs;
   wire [3:0]         prod_ALPHA_sh;
   wire [3:0]         squareb_wxs;
   wire [3:0]         inv_in_wxs;
   wire [5:0]         x;
   
   
   // aplpha*sh
   generate
     if (POLY1 == 3)  begin
       assign       x = {2{ALPHA[3]}} ^ {ALPHA[2],ALPHA[0]};
       assign       prod_ALPHA_sh[0] = ~(ALPHA[0] & sh_in_rxs[0]) ^ ~(ALPHA[3] & sh_in_rxs[1]) ^  ~(ALPHA[2] & sh_in_rxs[2]) ^ ~(ALPHA[1] & sh_in_rxs[3]);
       assign       prod_ALPHA_sh[1] = ~(ALPHA[1] & sh_in_rxs[0]) ^ ~(x[0] & sh_in_rxs[1]) ^  ~(x[1] & sh_in_rxs[2]) ^ ~((ALPHA[2] 
                                       ^ ALPHA[1]) & sh_in_rxs[3]);
       assign       prod_ALPHA_sh[2] = ~(ALPHA[2] & sh_in_rxs[0]) ^ ~(ALPHA[1] & sh_in_rxs[1]) ^  ~(x[0] & sh_in_rxs[2]) ^ ~(x[1] & sh_in_rxs[3]);
       assign       prod_ALPHA_sh[3] = ~(ALPHA[3] & sh_in_rxs[0]) ^ ~(ALPHA[2] & sh_in_rxs[1]) ^  ~(x[0] & sh_in_rxs[3]) ^ ~(ALPHA[1] & sh_in_rxs[2]);
   // sh^2 * (BETA)  
       assign squareb_wxs[3] = (sh_in_rxs[1] & BETA[1]) ^ (sh_in_rxs[0] & BETA[3]) ^ (sh_in_rxs[3] & BETA[1]) ^ (sh_in_rxs[2] & BETA[3]) 
	                       ^(sh_in_rxs[3] & BETA[0]) ^ (sh_in_rxs[3] & BETA[3]) ^ (sh_in_rxs[2] & BETA[2]);
       assign squareb_wxs[2] = (sh_in_rxs[1] & BETA[0]) ^ (sh_in_rxs[0] & BETA[2]) ^ (sh_in_rxs[2] & BETA[1]) ^ (sh_in_rxs[1] & BETA[3]) 
	                       ^(sh_in_rxs[3] & BETA[2]) ^ (sh_in_rxs[3] & BETA[0]) ^ (sh_in_rxs[2] & BETA[2]);
       assign squareb_wxs[1] = (sh_in_rxs[0] & BETA[1]) ^ (sh_in_rxs[2] & BETA[1]) ^ (sh_in_rxs[1] & BETA[3]) ^ (sh_in_rxs[3] & BETA[2]) 
	                       ^(sh_in_rxs[2] & BETA[0]) ^ (sh_in_rxs[1] & BETA[2]) ^ (sh_in_rxs[3] & BETA[2]) 
                               ^(sh_in_rxs[3] & BETA[1]) ^ (sh_in_rxs[2] & BETA[3]) ^ (sh_in_rxs[3] & BETA[3]);
       assign squareb_wxs[0] = (sh_in_rxs[2] & BETA[0]) ^ (sh_in_rxs[1] & BETA[2]) ^ (sh_in_rxs[3] & BETA[2]) ^ (sh_in_rxs[3] & BETA[1]) 
	                       ^(sh_in_rxs[2] & BETA[3]) ^ (sh_in_rxs[0] & BETA[0]);
     end

     if (POLY1 == 9)  begin

       assign       x[0] = ALPHA[3] ^ ALPHA[2];
       assign       prod_ALPHA_sh[0] = ~(ALPHA[0] & sh_in_rxs[0]) ^ ~(ALPHA[3] & sh_in_rxs[1]) ^  ~(x[0] & sh_in_rxs[2]) ^ ~((x[0] ^ ALPHA[1]) & sh_in_rxs[3]);
       assign       prod_ALPHA_sh[1] = ~(ALPHA[1] & sh_in_rxs[0]) ^ ~(ALPHA[0] & sh_in_rxs[1]) ^  ~(ALPHA[3] & sh_in_rxs[2]) ^ ~(x[0] & sh_in_rxs[3]);
       assign       prod_ALPHA_sh[2] = ~(ALPHA[2] & sh_in_rxs[0]) ^ ~(ALPHA[1] & sh_in_rxs[1]) ^  ~(ALPHA[0] & sh_in_rxs[2]) ^ ~(ALPHA[3] & sh_in_rxs[3]);
       assign       prod_ALPHA_sh[3] = ~(ALPHA[3] & sh_in_rxs[0]) ^ ~(x[0] & sh_in_rxs[1]) ^  ~((x[0]^ALPHA[1]) & sh_in_rxs[2]) ^ ~((x[0]^ALPHA[1]^ALPHA[0]) & sh_in_rxs[3]);

   // sh^2 * (BETA)  
       assign       x[1] = BETA[3] ^ BETA[2];
       assign       squareb_wxs[0] = ~(BETA[0] & (sh_in_rxs[0] ^ sh_in_rxs[2] ^ sh_in_rxs[3])) ^ ~(BETA[3] & (sh_in_rxs[3])) 
                                   ^ ~(x[1] & (sh_in_rxs[1] ^ sh_in_rxs[3])) ^ ~((x[1] ^ BETA[1]) & (sh_in_rxs[2] ^ sh_in_rxs[3]));
       assign       squareb_wxs[1] = ~(BETA[1] & (sh_in_rxs[0] ^ sh_in_rxs[2] ^ sh_in_rxs[3])) ^ ~(BETA[0] & sh_in_rxs[3]) 
                                   ^ ~(BETA[3] & (sh_in_rxs[1] ^ sh_in_rxs[3])) ^ ~(x[1] & (sh_in_rxs[2] ^ sh_in_rxs[3]));
       assign       squareb_wxs[2] = ~(BETA[2] & (sh_in_rxs[0] ^ sh_in_rxs[2] ^ sh_in_rxs[3])) ^ ~(BETA[1] & sh_in_rxs[3]) 
                                   ^ ~(BETA[0] & (sh_in_rxs[1] ^ sh_in_rxs[3])) ^ ~(BETA[3] & (sh_in_rxs[2] ^ sh_in_rxs[3]));
       assign       squareb_wxs[3] = ~(BETA[3] & (sh_in_rxs[0] ^ sh_in_rxs[2] ^ sh_in_rxs[3])) ^ ~(x[1] & sh_in_rxs[3]) 
                                   ^ ~((x[1]^BETA[1]) & (sh_in_rxs[1] ^ sh_in_rxs[3])) 
				   ^ ~((x[1]^BETA[1]^BETA[0]) & (sh_in_rxs[2] ^ sh_in_rxs[3]));
     end

     if (POLY1 == 15)  begin
       assign       x[0] = ALPHA[3] ^ ALPHA[2];
       assign       x[1] = ALPHA[3] ^ ALPHA[1];
       assign       x[2] = ALPHA[2] ^ ALPHA[1];
       assign       prod_ALPHA_sh[0] = ~(ALPHA[0] & sh_in_rxs[0]) ^ ~(ALPHA[3] & sh_in_rxs[1]) ^  ~(x[0] & sh_in_rxs[2]) ^ ~(x[2] & sh_in_rxs[3]);
       assign       prod_ALPHA_sh[1] = ~(ALPHA[1] & sh_in_rxs[0]) ^ ~((ALPHA[0] ^ ALPHA[3]) & sh_in_rxs[1]) ^  ~(ALPHA[2] & sh_in_rxs[2]) 
                                     ^ ~(x[1] & sh_in_rxs[3]);
       assign       prod_ALPHA_sh[2] = ~(ALPHA[2] & sh_in_rxs[0]) ^ ~(x[1] & sh_in_rxs[1]) ^  ~((ALPHA[0] ^ ALPHA[2]) & sh_in_rxs[2]) 
                                     ^ ~(ALPHA[1] & sh_in_rxs[3]);
       assign       prod_ALPHA_sh[3] = ~(ALPHA[3] & sh_in_rxs[0]) ^ ~(x[0] & sh_in_rxs[1]) ^  ~(x[2] & sh_in_rxs[2]) 
                                     ^ ~((ALPHA[1] ^ ALPHA[0]) & sh_in_rxs[3]);
   // sh^2 * (BETA)  
       assign       x[3] = BETA[3] ^ BETA[2];
       assign       x[4] = BETA[3] ^ BETA[1];
       assign       x[5] = BETA[2] ^ BETA[1];
       assign       squareb_wxs[0] = ~(BETA[0] & (sh_in_rxs[0] ^ sh_in_rxs[2])) ^ ~(BETA[3] & (sh_in_rxs[2] ^ sh_in_rxs[3])) 
                                   ^ ~(x[3] & (sh_in_rxs[1] ^ sh_in_rxs[2])) ^ ~(x[5] & sh_in_rxs[2]);
       assign       squareb_wxs[1] = ~(BETA[1] & (sh_in_rxs[0] ^ sh_in_rxs[2])) ^ ~((BETA[0] ^ BETA[3]) & (sh_in_rxs[2] ^ sh_in_rxs[3])) 
                                   ^  ~(BETA[2] & (sh_in_rxs[1] ^ sh_in_rxs[2])) ^ ~(x[4] & sh_in_rxs[2]);
       assign       squareb_wxs[2] = ~(BETA[2] & (sh_in_rxs[0] ^ sh_in_rxs[2])) ^ ~(x[4] & (sh_in_rxs[2] ^ sh_in_rxs[3])) 
                                   ^  ~((BETA[0] ^ BETA[2]) & (sh_in_rxs[1] ^ sh_in_rxs[2])) ^ ~(BETA[1] & sh_in_rxs[2]);
       assign       squareb_wxs[3] = ~(BETA[3] & (sh_in_rxs[0] ^ sh_in_rxs[2])) ^ ~(x[3] & (sh_in_rxs[2] ^ sh_in_rxs[3])) 
                                     ^  ~(x[5] & (sh_in_rxs[1] ^ sh_in_rxs[2])) ^ ~((BETA[1] ^ BETA[0]) & sh_in_rxs[2]);
     end
   endgenerate

   // sl + sh
   assign             sum_sl_sh_wxs = prod_ALPHA_sh ^ sl_in_rxs;
   // sq_b + mul1
   assign             inv_in_wxs = squareb_wxs ^ mul1_out_wxs;

   // module instantiations
   A_cse_ocs_dpa_aes10c_gf16_mul #(.POLY1(POLY1)) mul1 ( .a(sl_in_rxs[3:0]),     .b( sum_sl_sh_wxs[3:0]), .o(mul1_out_wxs[3:0]));
   A_cse_ocs_dpa_aes10c_gf16_mul #(.POLY1(POLY1)) mul2 ( .a(sh_in_rxs[3:0]),     .b( inv_out_rxs[3:0]),   .o(sh_out_wxs[3:0]));
   A_cse_ocs_dpa_aes10c_gf16_mul #(.POLY1(POLY1)) mul3 ( .a(sum_sl_sh_wxs[3:0]), .b( inv_out_rxs[3:0]),   .o(sl_out_wxs[3:0]));
   A_cse_ocs_dpa_aes10c_gf16_inv #(.POLY1(POLY1)) inv	( .a(inv_in_wxs[3:0]), .o( inv_out_rxs[3:0] ));

endmodule
